package abak.tr.com.boxedverticalseekbar;

import abak.tr.com.boxedverticalseekbar.utils.AttrUtils;
import abak.tr.com.boxedverticalseekbar.utils.PxUtil;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

public class BoxedVertical extends Component implements Component.DrawTask,
        Component.EstimateSizeListener, Component.TouchEventListener {
    private static final int MAX = 100;
    private static final int MIN = 0;
    /**
     * Text size in SP.
     */
    private float mTextSize = 26;
    /**
     * The Maximum value that this SeekArc can be set to
     */
    private int mMax = MAX;
    /**
     * The min value of progress value.
     */
    /**
     * The increment/decrement value for each movement of progress.
     */
    /**
     * The corner radius of the view.
     */
    private int mCornerRadius = 10;
    /**
     * Text bottom padding in pixel.
     */
    private int mtextBottomPadding = 20;
    /**
     * The increment/decrement value for each movement of progress.
     */
    private int mStep = 10;
    private int mMin = MIN;
    private int mPoints;
    private int progressColor;
    private int backgroundColor;
    private int textColor;
    private boolean mEnabled = true;
    private boolean mTouchDisabled = true;
    private boolean mtextEnabled = true;
    private boolean firstRun = true;
    private boolean mImageEnabled = false;
    private Paint mProgressPaint;
    private Paint mTextPaint;
    private int scrHeight;
    private int scrWidth;
    private float mProgressSweep = 0;
    private final HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MY_TAG");
    private PixelMap mDefaultImage;
    private PixelMap mMinImage;
    private PixelMap mMaxImage;
    private OnValuesChangeListener mOnValuesChangeListener;

    public BoxedVertical(Context context) {
        this(context, null);
    }

    public BoxedVertical(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public BoxedVertical(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        setEstimateSizeListener(this::onEstimateSize);
        setTouchEventListener(this::onTouchEvent);
        init(attrSet);
        addDrawTask(this);
    }

    private void init(AttrSet attrSet) {
        mPoints = mMax / 2;
        if (attrSet != null) {
            mMax = AttrUtils.getInteger(attrSet, "max", mMax);
            mMin = AttrUtils.getInteger(attrSet, "min", mMin);
            mPoints = AttrUtils.getInteger(attrSet, "defaultValue", mMax / 2);
            mCornerRadius = AttrUtils.getInteger(attrSet, "libCornerRadius", mCornerRadius);
            mtextBottomPadding = AttrUtils.getInteger(attrSet, "textBottomPadding", mtextBottomPadding);
            mStep = AttrUtils.getInteger(attrSet, "step", mStep);
            progressColor = AttrUtils.getColor(attrSet, "progressColor", ResourceTable.Color_color_progress);
            backgroundColor = AttrUtils.getColor(attrSet, "backgroundColor", ResourceTable.Color_color_background);
            mTextSize = AttrUtils.getFloat(attrSet, "defaulTextSize", mTextSize);
            textColor = AttrUtils.getColor(attrSet, "textColor", ResourceTable.Color_color_text);
            mEnabled = AttrUtils.getBoolean(attrSet, "enabled", mEnabled);
            mTouchDisabled = AttrUtils.getBoolean(attrSet, "touchDisabled", mTouchDisabled);
            mtextEnabled = AttrUtils.getBoolean(attrSet, "textEnabled", mtextEnabled);
            mImageEnabled = AttrUtils.getBoolean(attrSet, "imageEnabled", mImageEnabled);
            if (mImageEnabled) {
                try {
                    mDefaultImage = AttrUtils.getName(attrSet, "imgResourceDefault").getPixelMap();
                    mMinImage = AttrUtils.getName(attrSet, "imgResourceMin").getPixelMap();
                    mMaxImage = AttrUtils.getName(attrSet, "imgResourceMax").getPixelMap();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        // range check
        mPoints = (mPoints > mMax) ? mMax : mPoints;
        mPoints = (mPoints < mMin) ? mMin : mPoints;
        mProgressPaint = new Paint();
        mProgressPaint.setColor(new Color(progressColor));
        mProgressPaint.setAntiAlias(true);
        mProgressPaint.setStyle(Paint.Style.STROKE_STYLE);
        mTextPaint = new Paint();
        mTextPaint.setColor(new Color(textColor));
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL_STYLE);
        mTextSize = PxUtil.fp2px(mTextSize);
        mTextPaint.setTextSize((int) mTextSize);
    }

    @Override
    public void addDrawTask(DrawTask task) {
        super.addDrawTask(task);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        Paint paint = new Paint();
        paint.setAlpha(255);
        canvas.translate(0, 0);
        Path mPath = new Path();
        mPath.addRoundRect(new RectFloat(0, 0, scrWidth, scrHeight), mCornerRadius,
                mCornerRadius, Path.Direction.COUNTER_CLOCK_WISE);
        canvas.clipPath(mPath, Canvas.ClipOp.INTERSECT);
        paint.setColor(new Color(backgroundColor));
        paint.setAntiAlias(true);
        canvas.drawRect(0, 0, scrWidth, scrHeight, paint);
        if (firstRun) {
            getmProgressSweep(mPoints);
            firstRun = false;
        }
        canvas.drawLine(new Point((float) (double)scrWidth / 2, scrHeight + getMarginTop()), new Point((float) (double)scrWidth / 2, mProgressSweep), mProgressPaint);
        if (mImageEnabled && mDefaultImage != null && mMinImage != null && mMaxImage != null) {
            //If image is enabled, text will not be shown
            if (mPoints == mMax) {
                drawIcon(mMaxImage, canvas);
            } else if (mPoints == mMin) {
                drawIcon(mMinImage, canvas);
            } else {
                drawIcon(mDefaultImage, canvas);
            }
        } else {
            // If image is disabled and text is enabled show text
            if (mtextEnabled) {
                String strPoint = String.valueOf(mPoints);
                drawText(canvas, mTextPaint, strPoint);
            }
        }
    }

    /**
     * 获取文字的宽度和高度
     *
     * @param canvas 画布
     * @param paint  画笔
     * @param text   要画的内容
     */
    private void drawText(Canvas canvas, Paint paint, String text) {
        int[] textWH = getTextWH(text, paint);
        canvas.drawText(paint, text, scrWidth / 2 - textWH[0] / 2, scrHeight - textWH[1]);
    }

    private int[] getTextWH(String text, Paint paint) {
        if (text == null || text.length() < 1) {
            return new int[]{0, 0};
        }
        Rect textBounds = paint.getTextBounds(text);
        int width = textBounds.left + textBounds.getWidth();
        int height = textBounds.bottom + textBounds.getHeight();
        return new int[]{width, height};
    }

    private void drawIcon(PixelMap bitmap, Canvas canvas) {
        int width = bitmap.getImageInfo().size.width;
        int height = bitmap.getImageInfo().size.height;
        RectFloat rectFloat = new RectFloat((scrWidth / 2) - (width / 2),
                scrHeight - height, (scrWidth / 2) + width / 2, scrHeight);
        Paint paint = new Paint();
        canvas.drawPixelMapHolderRect(new PixelMapHolder(bitmap), rectFloat, paint);
    }

    @Override
    public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
        scrWidth = EstimateSpec.getSize(widthEstimateConfig);
        scrHeight = EstimateSpec.getSize(heightEstimateConfig);
        HiLog.info(label, "scrWidth==" + scrWidth + "==getMarginTop()=" + getMarginTop() + "==getMarginBottom()=" + getMarginBottom() + "==scrHeight=" + scrHeight);
        mProgressPaint.setStrokeWidth(scrWidth);
//        setEstimatedSize(Component.EstimateSpec.getChildSizeWithMode(scrWidth, scrWidth, Component.EstimateSpec.NOT_EXCEED),
//                Component.EstimateSpec.getChildSizeWithMode(scrHeight, scrHeight, Component.EstimateSpec.NOT_EXCEED));
        return false;
    }

    /**
     * 设置当前值
     *
     * @param points 传入真实值
     */
    public void setValue(int points) {
        points = points > mMax ? mMax : points;
        points = points < mMin ? mMin : points;
        updateProgressByValue(points);
    }

    private void updateProgressByValue(int value) {
        mPoints = value;
        mPoints = (mPoints > mMax) ? mMax : mPoints;
        mPoints = (mPoints < mMin) ? mMin : mPoints;
        getmProgressSweep(mPoints);
        if (mOnValuesChangeListener != null) {
            mOnValuesChangeListener.onPointsChanged(this, mPoints);
        }
        invalidate();
    }

    private void getmProgressSweep(int mPoints) {
        //convert min-max range to progress
        mProgressSweep = (float) ((mPoints - mMin) * (double)scrHeight / (mMax - mMin));
        //reverse value because progress is descending
        mProgressSweep = (int)((double)scrHeight - (double)mProgressSweep);
    }

    /**
     * 设置监听回调
     *
     * @param onValuesChangeListener 回调对象
     */
    public void setOnBoxedPointsChangeListener(OnValuesChangeListener onValuesChangeListener) {
        mOnValuesChangeListener = onValuesChangeListener;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
        if (mEnabled) {
            switch (event.getAction()) {
                case TouchEvent.PRIMARY_POINT_DOWN:
                    if (mOnValuesChangeListener != null) {
                        mOnValuesChangeListener.onStartTrackingTouch(this);
                    }
                    if (!mTouchDisabled) {
                        updateOnTouch(event);
                    }
                    break;
                case TouchEvent.POINT_MOVE:
                    updateOnTouch(event);
                    break;
                case TouchEvent.PRIMARY_POINT_UP:
                    if (mOnValuesChangeListener != null) {
                        mOnValuesChangeListener.onStopTrackingTouch(this);
                    }
                    setPressState(false);
                    break;
                case TouchEvent.CANCEL:
                    if (mOnValuesChangeListener != null) {
                        mOnValuesChangeListener.onStopTrackingTouch(this);
                    }
                    setPressState(false);
                    break;
            }
            return true;
        }
        return false;
    }

    private void updateOnTouch(TouchEvent event) {
        setPressState(true);
        double mTouch = PxUtil.convertTouchEventPointValue(scrHeight, event.getPointerPosition(0).getY());
//        int marginBottom = getMarginBottom();
        int progress = PxUtil.mathValue(mTouch);
        mProgressSweep = progress;
        updateProgress(progress);
    }

    private void updateProgress(int progress) {
        progress = PxUtil.getProgress(scrHeight, progress);
        // convert progress to min-max range
        // reverse value because progress is descending
        mPoints = PxUtil.getPoints(scrHeight, progress, mMax, mMin);
        HiLog.info(label, "mPoints==" + mPoints + "-------mProgressSweep==" + mProgressSweep);
        //if value is not max or min, apply step
        if (mPoints != mMax && mPoints != mMin) {
            mPoints = mPoints - (mPoints % mStep) + (mMin % mStep);
        }
        if (mOnValuesChangeListener != null) {
            mOnValuesChangeListener
                    .onPointsChanged(this, mPoints);
        }
        invalidate();
    }

    /**
     * 获取当前值
     *
     * @return int值
     */
    public int getValue() {
        return mPoints;
    }

    /**
     * 设置自定义控件圆角半径
     *
     * @param mRadius int半径
     */
    public void setCornerRadius(int mRadius) {
        this.mCornerRadius = mRadius;
        invalidate();
    }

    /**
     * 获取最大值
     *
     * @return 返回设置的最大值
     */
    public int getMax() {
        return mMax;
    }

    /**
     * 设置最大值
     *
     * @param mMax 传入最大值
     * @throws IllegalArgumentException 最大值小于0
     */
    public void setMax(int mMax) {
        if (mMax <= mMin) {
            throw new IllegalArgumentException("Max should not be less than zero");
        }
        this.mMax = mMax;
    }

    /**
     * 获取是否可滑动
     *
     * @return 是否可滑
     */
    public boolean isEnabled() {
        return mEnabled;
    }

    /**
     * 设置是否可滑动
     *
     * @param enabled 滑动
     */
    public void setEnabled(boolean enabled) {
        this.mEnabled = enabled;
    }

    /**
     * 设置默认数值
     *
     * @param mDefaultValue 默认值
     * @throws IllegalArgumentException 默认值大于最大值
     */
    public void setDefaultValue(int mDefaultValue) {
        if (mDefaultValue > mMax) {
            throw new IllegalArgumentException("Default value should not be bigger than max value.");
        }
        this.mPoints = mDefaultValue;

    }

    /**
     * 获取当前精度
     *
     * @return 滑动精度
     */
    public int getStep() {
        return mStep;
    }

    /**
     * 设置当前精度
     *
     * @param step 精度
     */
    public void setStep(int step) {
        mStep = step;
    }

    /**
     * 监听类
     */
    public interface OnValuesChangeListener {
        /**
         * Notification that the point value has changed.
         *
         * @param boxedPoints The SwagPoints view whose value has changed
         * @param points      The current point value.
         */
        void onPointsChanged(BoxedVertical boxedPoints, int points);

        void onStartTrackingTouch(BoxedVertical boxedPoints);

        void onStopTrackingTouch(BoxedVertical boxedPoints);
    }
}
